/**
 * 时间日期转化工具
<<<<<<< HEAD
 * @author lms 2016.07.14 
=======
 *
 * @author lms 2016.07.14
>>>>>>> 05b463766f00653d87707d9aa4f82c36905a26c0
 * last update by lms 在获取时区时间，采用 LocalDateTime 进行相关操作。
 */
package ms.core.tool;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class DateTimeTool {

    /**
     * Date转LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate().atStartOfDay();

        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * Date转LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate dateToLocalDate(Date date) {
        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate();
        else
            return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * LocalDateTime转Date
     *
     * @param localDateTime
     * @return
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        if (localDateTime == null)
            return null;

        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDate转Date
     *
     * @param date
     * @return
     */
    public static Date localDateToDate(LocalDate localDate) {
        if (localDate == null)
            return null;

        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Date 转指定格式字符串日期时间
     * @param dt date of to be formated
     * @param format string,such as: "yyyy-MM-dd HH:mm:ss"
     * @return: success return string datetime, otherwise return null
     */
    public static String formatDateTime(Date dt, String format) {
        if (dt == null) return null;

        DateTimeFormatter fmt = DateTimeFormatter.ofPattern(format);
        return fmt.format(dateToLocalDateTime(dt));
    }

    /**
     * Date 转指定格式字符串日期时间
     * @param dt date of to be formated
     * @param format string,such as: "yyyy-MM-dd"
     * @return: success return string datetime, otherwise return null
     */
    public static String formatDate(Date dt, String format) {
        if (dt == null) return null;

        DateTimeFormatter fmt = DateTimeFormatter.ofPattern(format);
        return fmt.format(dateToLocalDate(dt));
    }

    /**
     * Date 转日期 yyyy-MM-dd 格式字符串
     * @param date to format date
     * @return null when exception throw or return date string
     */
    public static String dateToDateString(Date date) {
        return formatDate(date, "yyyy-MM-dd");
    }

    /**
     * Date类型转时间字符串
     * @param date to format date
     * @return null when exception throw or return time string
     */
    public static String dateToTimeString(Date date) {
        return formatDateTime(date, "HH:mm:ss");
    }

    /**
     * Date类型转时间日期格式字符串
     * @param date to format date
     * @return null when exception throw or return time string
     */
    public static String dateToDateTimeString(Date date) {
        return formatDateTime(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * Date 转按年月日时分秒格式长整型
     * @param date
     * @return yyyyMMddHHmmss long value
     */
    public static long datetimeToLong(Date date) {
        String value = formatDateTime(date, "yyyyMMddHHmmss");
        return Long.parseLong(value);
    }

    /**
     * yyMMddHHmmss 或 yyyyMMddHHmmss 长整型日期转 Date
     * @param dateTime 待转化的长整型日期时间编码值
     * @return null if invalid nDate, or return yyyy-MM-dd HH:NN:SS string
     */
    public static Date longToDatetime(long dateTime) {
        String str = String.valueOf(dateTime);
        if (str.length() != 12 && str.length() != 14)
            return null;

        if (str.length() == 12)
            str = "20" + str;

        char[] cdt = str.toCharArray();
        String tmp = String.valueOf(cdt, 0, 4) + "-" + cdt[4] + cdt[5] + "-" + cdt[6] + cdt[7] + " " +
                cdt[8] + cdt[9] + ":" + cdt[10] + cdt[11] + ":" + cdt[12] + cdt[13];

        return strToDateTime(tmp);
    }

    /**
     * 年月日格式整型转Date类型
     * @param nDate yyyyMMdd格式的整型数
     * @return Date 日期
     */
    public static Date intToDate(int nDate) {
        String strDate = null;
        char[] tmp = String.valueOf(nDate).toCharArray();
        if (tmp.length == 8) {
            strDate = String.valueOf(tmp, 0, 4) + "-" + tmp[4] + tmp[5] + "-" + tmp[6] + tmp[7];
        }

        return strToDate(strDate);
    }

    /**
     * Date 按年月日格式转整型
     * @param date
     * @return yyyyMMdd int value
     */
    public static int dateToInt(Date date) {
        String value = formatDate(date, "yyyyMMdd");
        return Integer.parseInt(value);
    }

    /**
     * Date转unix时间戳
     * @param date
     * @return unix time
     */
    public static long dateToUnixTime(Date date) {
        if (date == null) return 0;

        return date.getTime() / 1000;
    }

    /**
     * unix时间戳转Date
     * @param unix time
     * @return date
     * @throws Exception
     */
    public static Date unixTimeToDate(long unix) {
        if (unix < 1) return null;

        if (String.valueOf(unix).length() == 10)
            unix = unix * 1000;

        return new Date(unix);
    }

    /**
     * 字符串转日期
     * @param str 格式: "yyyy-MM-dd" 或 "yyyy.MM.dd"，如果日期只到月份，则默认为月首日
     * @return
     */
    public static Date strToDate(String str) {
        if (StrTool.isBlank(str)) return null;
        str = str.replaceAll("\\.", "-");
        List<String> list = StrTool.splitString(str, "-");
        if (list.size() < 3) list.add("01");
        if (list.get(0).length() < 4) list.set(0, "20" + list.get(0));
        if (list.get(1).length() < 2) list.set(1, "0" + list.get(1));
        if (list.get(2).length() < 2) list.set(2, "0" + list.get(2));
        str = list.get(0) + "-" + list.get(1) + "-" + list.get(2);

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        try {
            return localDateToDate(LocalDate.parse(str, dtf));
        } catch (Exception e) {
            Log4j2Tool.error(DateTimeTool.class, "strToDate:", e);
            return null;
        }
    }

    /**
     * 字符串转日期时间
     * @param str it's format is that: "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-ddTHH:mm:ss"
     * @return
     */
    public static Date strToDateTime(String str) {
        if (StrTool.isBlank(str)) return null;

        if (str.length() == 16) str += ":00";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        return localDateTimeToDate(LocalDateTime.parse(str, dtf));
    }

    /**
     * utc字符串日期时间转Date, UTC时间加8个小时=北京时间
     * @param utcTime 如20212-03-25T10:10:15Z
     * @return
     */
    public static Date utcToDate(String utcTime) {
        DateTimeFormatter dtf;
        int len = utcTime.length();
        if (len >= 22) {
            if (len == 22)
                dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.S'Z'");
            else
                dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        } else {
            dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
        }

        LocalDateTime local = LocalDateTime.parse(utcTime, dtf);
        Date zeroDateTime = DateTimeTool.localDateTimeToDate(local);
        return DateTimeTool.incHour(zeroDateTime, 8);
    }

    /**
     * utc字符串日期时间转Date
     * @param utcTime 如20212-03-25T10:10:15Z
     * @return
     */
    public static Date iso8601ToDate(String iso8601Time) {
        DateTimeFormatter dtf;
        if (iso8601Time.length() > 21)
            dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        else
            dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");

        LocalDateTime local = LocalDateTime.parse(iso8601Time, dtf);
        return DateTimeTool.localDateTimeToDate(local);
    }

    /**
     * 北京时间 Date 转utc时间(北京时间-8小时)字符串
     * @author lms 2021.03.25
     * @param date 待转成utc日期时间字符串的Date
     * @return
     */
    public static String toUtcTime(Date date) {
        ZoneId utcZone = ZoneId.of("UTC");
        LocalDateTime localTime = date.toInstant().atZone(utcZone).toLocalDateTime();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        return localTime.format(formatter);
    }

    /**
     * 计算指定时区跟计算机所在地时区时间差(秒)
     *
     * @param timezone 指定时区
     * @return value=系统时区当前时间代表秒数-指定时区当前时间代表的秒数
     */
    private static long getTimezoneOffset(String timezone) {
        Date now = new Date();
        LocalDateTime localTime = now.toInstant()
                .atZone(ZoneId.systemDefault())
                .withZoneSameInstant(ZoneId.of(timezone))
                .toLocalDateTime();
        Date zoneTime = DateTimeTool.localDateTimeToDate(localTime);
        return (now.getTime() - zoneTime.getTime()) / 1000;
    }

    /**
     * 日期转化成指定时区时间
     *
     * @param timezone 指定时区
     * @param date 指定日期时间
     * @return 指定时区日期时间
     */
    public static Date getTimezoneDate(String timezone, Date date) {
        long offset = getTimezoneOffset(timezone);
        return DateTimeTool.incSecond(new Date(date.getTime()), -(int) offset);
    }

    /**
     * 增加时间
     * @param date Date to add time
     * @param field to indicate which part to increase
     * @param amount date's field to add amount
     * @return new Date if successful, otherwise return null
     */
    private static Date incTime(Date date, int field, int amount) {
        if (date == null) return null;

        try {
            Calendar cd = Calendar.getInstance();
            cd.setTime(date);
            cd.add(field, amount);
            return cd.getTime();
        } catch (Exception e) {
            Log4j2Tool.error(DateTimeTool.class, "incTime:", e);
        }
        return null;
    }

    /**
     * 已有时间增加指定年数
     * @param dt Date to increse year
     * @param days number of year to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incYear(Date date, int years) {
        return incTime(date, Calendar.YEAR, years);
    }

    /**
     * 已有时间增加指定月数
     * @param date Date to increse month
     * @param days number of month to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incMonth(Date date, int months) {
        return incTime(date, Calendar.MONTH, months);
    }

    /**
     * 已有时间增加指定天数
     * @param date Date to increse day
     * @param days number of day to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incDay(Date date, int days) {
        return incTime(date, Calendar.DATE, days);
    }

    /**
     * 已有时间增加指定小时数
     * @param date Date to increse hour
     * @param hours number of hours to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incHour(Date date, int hours) {
        return incTime(date, Calendar.HOUR, hours);
    }

    /**
     * 已有时间增加指定分钟数
     * @param date Date to increse minute
     * @param minutes number of minutes to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incMinute(Date date, int minutes) {
        return incTime(date, Calendar.MINUTE, minutes);
    }

    /**
     * 已有时间增加指定秒数
     * @param date Date to increse second
     * @param minutes number of seconds to increase
     * @return incresed Date if successful, otherwise return null
     */
    public static Date incSecond(Date date, int seconds) {
        return incTime(date, Calendar.SECOND, seconds);
    }

    /**
     * 计算起止时间间隔的秒数
     * @param now
     * @param then
     * @return seconds between now and then
     */
    public static long secondsBetween(Date now, Date then) {
        long t = now.getTime() - then.getTime();
        return (long) (t / 1000);
    }

    /**
     * 计算起止时间间隔的分钟数
     * @param now
     * @param then
     * @return seconds between now and then
     */
    public static long minutesBetween(Date now, Date then) {
        return secondsBetween(now, then) / 60;
    }

    /**
     * 计算起止时间间隔的小时数
     * @param now
     * @param then
     * @return seconds between now and then
     */
    public static long hoursBetween(Date now, Date then) {
        return minutesBetween(now, then) / 60;
    }

    /**
     * 计算起止时间间隔的天数
     * @param now
     * @param then
     * @return seconds between now and then
     */
    public static long daysBetween(Date now, Date then) {
        return minutesBetween(now, then) / (60 * 24);
    }

    /**
     * Dephi的double日期类型转Date
     * @param dTime delphi类型日期 TDateTime = type double;
     * @return
     */
    public static Date doubleToDate(Double dTime) {
        Calendar cd = Calendar.getInstance();
        //Delphi的日期类型从1899-12-30开始
        cd.set(1899, 11, 30, 0, 0, 0);
        cd.add(Calendar.DATE, dTime.intValue());
        cd.add(Calendar.MILLISECOND, (int) ((dTime % 1) * 24 * 60 * 60 * 1000));
        return cd.getTime();
    }

    /**
     * 时间转double
     *
     * @param date
     * @return 返回值类似：43322.3770190278
     */
    public static double dateToDouble(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        long localOffset = -(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET));
        double dd = (double) (date.getTime() - localOffset) / 24 / 3600 / 1000 + 25569.0000000;
        //先默认保留10位小数
        DecimalFormat df = new DecimalFormat("#.0000000000");
        return Double.valueOf(df.format(dd));
    }

    /**
     * 获取指定日期当月第一天日期
     * @param date
     * @return
     * by @author lms 2017.03.08
     */
    public static Date startOfTheMonth(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        cd.set(Calendar.DATE, 1);
        return cd.getTime();
    }

    /**
     * 获取指定日期当月最后一天日期
     * @param date
     * @return
     * by @author lms 2017.03.08
     */
    public static Date endOfTheMonth(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        cd.set(Calendar.DATE, 1);
        cd.roll(Calendar.DATE, -1);
        return cd.getTime();
    }

    /**
     * 时间日期截断时间
     * @param date
     * @return
     * @author lms 2017.03.08
     */
    public static Date truncateTime(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        cd.set(Calendar.HOUR_OF_DAY, 0);
        cd.set(Calendar.MINUTE, 0);
        cd.set(Calendar.SECOND, 0);
        cd.set(Calendar.MILLISECOND, 0);

        return cd.getTime();
    }

    /**
     * 获取指定日期的月份共有几天
     * @param date
     * @return
     * @author lms 2017.05.25
     */
    public static int daysOfTheMonth(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        cd.set(Calendar.DATE, 1);
        cd.roll(Calendar.DATE, -1);
        return cd.get(Calendar.DATE);
    }

    /**
     * 获取指定日期是当月第几天
     * @param date
     * @return
     * by @author lms 2017.05.25
     */
    public static int dayOf(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        return cd.get(Calendar.DATE);
    }

    /**
     * 获取指定日期在对应年度的月份(1-12)
     * @param date
     * @return 1~12
     * by @author lms 2017.05.25
     */
    public static int monthOf(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        return cd.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取指定日期的年份
     * @param date
     * @return
     * by @author lms 2017.05.25
     */
    public static int yearOf(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        return cd.get(Calendar.YEAR);
    }

    /**
     * 加n天
     * @param day
     * @param step
     * @return
     */
    public static int incrDayToInt(int day, int step) {
        return dateToInt(incDay(intToDate(day), step));
    }

    public static void main(String[] args) {
        System.out.println(DateTimeTool.strToDate("23.9.2"));
    }
}
